import { Vector2D } from "./Vector2D.js";

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

function shape(edges, x, y, step) {
  let p = new Vector2D(x, y);
  const dir = new Vector2D(step, 0);
  const res = [];
  const delta = Math.PI * (1 - (edges - 2) / edges);
  for (let i = 0; i < edges; i++) {
    p = p.copy().add(dir.rotate(delta));
    console.log(dir.dir);
    res.push(p);
  }
  return res;
}

/**
 *
 * @param {Array<Vector2D | number[]>} points
 */
function draw(
  points,
  { strokeStyle = "black", fillStyle = null, close = false } = {}
) {
  ctx.strokeStyle = strokeStyle;
  ctx.beginPath();
  ctx.moveTo(...points[0]);
  for (let i = 1; i < points.length; i++) {
    ctx.lineTo(...points[i]);
  }
  if (close) ctx.closePath();
  if (fillStyle) {
    ctx.fillStyle = fillStyle;
    ctx.fill();
  }
  ctx.stroke();
}

const TAU_SEGMENTS = 60;
const TAU = Math.PI * 2;

function circle(x0, y0, radius, startAngle = 0, endAngle = Math.PI * 2) {
  return ellipse(x0, y0, radius, radius, startAngle, endAngle);
}

/**
 *
 * @param {number} x0
 * @param {number} y0
 * @param {number} radiusX
 * @param {number} radiusY
 * @param {number} startAngle
 * @param {number} endAngle
 * @returns number[][]
 */
function ellipse(
  x0,
  y0,
  radiusX,
  radiusY,
  startAngle = 0,
  endAngle = Math.PI * 2
) {
  const angle = Math.min(TAU, endAngle - startAngle);
  const ret = angle === TAU ? [] : [[x0, y0]];
  const segments = Math.round((TAU_SEGMENTS * angle) / TAU);
  for (let i = 0; i <= segments; i++) {
    const x = x0 + radiusX * Math.cos(startAngle + (angle * i) / segments);
    const y = y0 + radiusY * Math.sin(startAngle + (angle * i) / segments);
    ret.push([x, y]);
  }
  return ret;
}

const LINE_SEGMENTS = 600;

function parabola(x0, y0, p, min, max) {
  const ret = [];
  for (let i = 0; i <= LINE_SEGMENTS; i++) {
    const s = i / LINE_SEGMENTS;
    const t = min * (1 - s) + max * s;
    const x = x0 + 2 * p * t ** 2;
    const y = y0 + 2 * p * t;
    ret.push([x, y]);
  }
  return ret;
}

function parametric(xFunc, yFunc) {
  return function (start, end, seg = 100, ...args) {
    const points = [];
    for (let i = 0; i <= seg; i++) {
      const p = i / seg;
      const t = start * (1 - p) + end * p;
      const x = xFunc(t, ...args);
      const y = yFunc(t, ...args);
      points.push([x, y]);
    }
    return {
      draw: draw.bind(null, points),
      points,
    };
  };
}

ctx.translate(250, 250);
ctx.scale(1, -1);
// draw(shape(6, 0, 0, 100))
// draw(circle(0, 0, 100))
// draw(ellipse(0, 0, 100, 50, Math.PI / 3))
// draw(parabola(0, 0, 5.5, -10, 10))
// draw(parabola(0, 0, 115.5, -10, 10))

const para = parametric(
  (t) => 25 * t,
  (t) => 25 * t ** 2
);

const cir = parametric(
  (t, x0, y0, radius) => x0 + Math.cos(t) * radius,
  (t, x0, y0, radius) => y0 + Math.sin(t) * radius
);

const helical = parametric(
  (t, l) => l * t * Math.cos(t),
  (t, l) => l * t * Math.sin(t)
);

const star = parametric(
  (t, l) => l * Math.cos(t) ** 3,
  (t, l) => l * Math.sin(t) ** 3
);

const quadricBezier = parametric(
  (t, [{ x: x0 }, { x: x1 }, { x: x2 }]) =>
    (1 - t) ** 2 * x0 + 2 * t * (1 - t) * x1 + t ** 2 * x2,
  (t, [{ y: y0 }, { y: y1 }, { y: y2 }]) =>
    (1 - t) ** 2 * y0 + 2 * t * (1 - t) * y1 + t ** 2 * y2
);

const cubicBezier = parametric(
  (t, [{ x: x0 }, { x: x1 }, { x: x2 }, { x: x3 }]) =>
    (1 - t) ** 3 * x0 +
    3 * t * (1 - t) ** 2 * x1 +
    3 * (1 - t) * t ** 2 * x2 +
    t ** 3 * x3,
  (t, [{ y: y0 }, { y: y1 }, { y: y2 }, { y: y3 }]) =>
    (1 - t) ** 3 * y0 +
    3 * t * (1 - t) ** 2 * y1 +
    3 * (1 - t) * t ** 2 * y2 +
    t ** 3 * y3
);

const p0 = new Vector2D(0, 0);
const p1 = new Vector2D(100, 0);
p1.rotate(2);
const p2 = new Vector2D(150, 0);
p2.rotate(-1)
const p3 = new Vector2D(200, 0)
const count = 30;

for (let i = 0; i < count; i++) {
  p1.rotate((2 / count) * Math.PI);
  p2.rotate((2 / count) * Math.PI);
  p3.rotate((2 / count) * Math.PI);
  cubicBezier(0, 1, 100, [p0, p1, p2, p3]).draw();
}

// star(0, Math.PI * 2, 50, 150).draw({
//   strokeStyle: "red",
// });

// helical(0, 50, 500, 5).draw({
//   strokeStyle: "blue",
// });

cir(0, Math.PI * 2, 5, 0, 0, 50).draw({
  strokeStyle: "red",
});

// para(-5.5, 5.5).draw();
